Resource Hints and Debouncing

Let's learn about how resource hints can help us improve our load times.

Introduction#

User experience (UX) and user interface (UI) are among the most important aspects to consider when creating any application. It’s imperative to make the UX and UI as smooth and hassle-free as possible for the end user. To that end, several front-end optimization techniques are used by developers to ensure a smooth UX. Front-end optimizations might often be considered low-priority tasks when talking about API design. However, that is not the case, because the UI is what the user will interact with and what will connect the user with the API. This is why it’s important to understand front-end optimization techniques. In this lesson, we’ll focus on resource hints, specifically prefetching, preloading, and preconnecting.

Resource hints#

Resource hints are hints or instructions that instruct the browser on managing specific resources—such as web pages, images, and so on—as well as which resources need priority in being fetched and rendered. They’re implemented using the rel attribute of the link element in HTML, as shown in the code snippet below. These hints are implemented by the front-end developers in their HTML pages.

Examples of how to use resource hints

Let’s now discuss each of these in more detail.

Prefetching#

Prefetching is a resource hint that allows the browser to fetch resources that might be needed later. These resources are stored in the browser's cache. Let's imagine that a user is scrolling on the home page of a video-sharing website. It’s expected that a user will click on a video that will interest them. In this case, we would want to improve the subsequent video's load time by fetching the video in advance and storing it in the browser's cache. The prefetch directive is well suited for that. This example has been visualized in the illustration below

An example of how prefetching helps reduce latency
An example of how prefetching helps reduce latency

In the example above there are two scenarios, one with prefetching and one without. Without prefetching, the video request is only sent when the user clicks on the video. However, with prefetching, the video is fetched in advance, reducing latency.

The prefetch directive is implemented in the following way:

Prefetching in HTML

In the HTML snippet given above, we see that the browser has been instructed to fetch the page with the news story and the cover photo from the back-end API in advance. These resources have been provided in the href. We do this because we anticipate the user will want to click on an interesting headline to know more.

DNS prefetching is another type of prefetching. This resource hint notifies the client that there are assets that may be required later on from a different URL. This tells the browser to resolve the URL quickly. Let’s suppose that we need an image from the URL example.com, this is how we’ll represent it:

<link rel="dns-prefetch" href="//example.com">

If we don’t do this, the browser (upon realizing the need for this resource) would first have to resolve the DNS, and that can potentially cause a delay. By implementing DNS prefetching, we have eliminated the potential delay.

The prefetch resource hint has a low download priority. This means that it doesn’t interfere with other resources and can be used to prefetch quite a few things. For this reason, it’s best used when we have to load resources later and not immediately. The example of a video site that we discussed earlier is an optimal use case of prefetching because the files needed for the videos can be fetched while the user is scrolling the landing page. Netflix has used prefetch effectively to reduce their Time to Interactive (TTI) by 30%.

Prefetching is not a perfect method. Despite the low download priority, this directive can still increase data consumption, which itself can cause issues for users that are on a limited data plan. Furthermore, we can end up loading extra bytes that might prove to be useless. Because of the low download priority, it isn’t recommended to be used when we need to load urgent resources. For that, we’ll use the `preload` resource hint, which we’ll discuss later.

Let's look at what we've learned so far in the summary table below:

Resource Hint

Pros

Cons


Prefetching

  • Improves load times of subsequent pages
  • Has low download priority so it doesn't interfere with other resources
  • May increase data consumption
  • The fetched data might remain unused
  • Not recommended to be used for urgent resources

Preloading#

Preloading is used to force the browser to download a resource as soon as possible because we believe it to be crucial to the page. This technique can preload all the assets (images, fonts, and so on) through a certain script. It can also preload HTML pages with the JavaScript already executed and the CSS already applied. In doing so, the preloaded page will be rendered and displayed instantly. This directive is best used for resources that are imperative for the critical rendering path, such as fonts, CSS, and so on.

An example of preloading

The preload directive also requires the as attribute in its declaration, which is used to indicate the resource type that we wish to preload. The preload resource hint has a higher priority compared to prefetch and other resource hints. The browser may ignore other directives due to priority issues or a slow internet connection, but browsers must load the resources that are mentioned in the preload directive. Due to this, preloading is a powerful and useful instruction because it can allow us to make the browser download a resource immediately. We should use preload for resources considered critical to the current page, such as the main stylesheet, JavaScript files required for initial rendering, and web fonts.

Treebo is one of India's largest hotel chains and has reduced their TTI and Time to First Paint (T2F-Paint) by 1 second for their platform by preloading image headers and webpack bundles.

However, preloading has its own drawbacks. Overusing it can lead to it interfering with the other tasks that the browser has scheduled since preloading overrides the priorities set by the browser itself.

Let’s summarize what we’ve learned so far in the table below:

Resource Hint

Pros

Cons


Prefetching

  • Improves load times of subsequent pages
  • Has low download priority so it doesn't interfere with other resources
  • Increases data consumption
  • The fetched data might remain unused
  • Not recommended to be used for urgent resources


Preloading

  • Very useful in situations when a crucial resource for a page needs to be downloaded ASAP
  • Can mess with the set prioritizations and scheduling of the browser
Question

What is the difference between prefetching and preloading?

Hide Answer

The prefetch and preload resource hints are used to improve the performance of a website by allowing the browser to load resources in advance. However, they have different purposes and use cases.

Prefetching tells the browser to proactively fetch a resource and store it in the cache for future use. The goal of prefetch is to reduce the latency and loading time of a resource if it is needed later in the current or future sessions. The prefetch directive is typically used for resources that are not critical to the current page, but may be needed in the future.

Preloading tells the browser to fetch and process a resource as soon as possible. The goal of preload is to improve the loading performance of the current page by reducing the time it takes for a resource to become available. The preload directive is typically used for resources considered critical to the current page and should be loaded as soon as possible.

In summary, the main difference between prefetching and preloading is their intended use case. The prefetch directive is used to prepare the browser for future use, while preload is used to improve the performance of the current page.

Preconnecting#

Preconnecting helps the browser establish connections to a domain in advance. This is helpful when we might need to quickly download a resource from a domain, such as a website using fonts from Google Fonts or requesting a JSON response from an API.

When a browser sets up a connection to a third-party domain, it can often take hundreds of milliseconds. Setting a connection in advance can help us avoid that delay. If we are familiar with DNS prefetching, then we might think that this is very similar to preconnecting. However, the preconnect directive has a distinct advantage over the DNS prefetch. While performing the DNS lookups, preconnect performs the TLS negotiations and TCP handshakes as well while the DNS prefetch will only do the DNS lookup and will require a round trip to do the other steps.

An example of preconnect reducing the time to get resources from a third-party domain
An example of preconnect reducing the time to get resources from a third-party domain

This directive eliminates the round-trip latency and saves time. This can speed up the load times by 100–500 ms by establishing connections to third-party origins earlier. A few hundred milliseconds might seem insignificant, but they can make quite a bit of a difference in the way a user experiences the performance.

How to use the preconnect directive in HTML

The developers behind Chrome have managed to improve their TTI (Time To Interactive) by 1 second by using the preconnect directive to preconnect to important origins. As mentioned earlier, even a few hundred milliseconds can make a difference in the user's experience.

For this reason, preconnect is best used for resources that are hosted on a different domain from the main page, such as a CDN or an external API.

Preconnecting is a great tool to have in our arsenal, but setting up and maintaining a connection to stay open is a costly operation, both for the client and the server. So, while it may save time, it might cause extra CPU usage.

Let’s update what we’ve learned so far in the summary table below:

Resource Hint

Pros

Cons


Prefetching

  • Improves load times of subsequent pages
  • Has low download priority so it doesn't interfere with other resources
  • Increases data consumption
  • The fetched data might remain unused
  • Not recommended to be used for urgent resources


Preloading

  • Very useful in situations when a crucial resource for a page needs to be downloaded ASAP
  • Can mess with the set prioritizations and scheduling of the browser


Preconnect

  • Saves time by doing the TLS negotiations and TCP handshakes while performing the DNS lookups
  • Maintaining a connection is costly and uses more CPU

Debouncing#

The techniques mentioned above improve the UX by fetching and loading the resources in advance. However, while this is advantageous, it can also be misused by some users. Let’s imagine a user is scrolling through Facebook on their laptop, and suddenly their mouse or trackpad starts malfunctioning, causing it to keep scrolling on its own. Every time the end of the page is reached, the client makes a request for more content to be fetched, but because the mouse is still scrolling, this can cause the server to receive too many requests. As a result, it might cause the site to slow down because of all the functions that are attached to the scroll event.

Furthermore, all the data being fetched is useless because the user has moved elsewhere. This is, of course, an extreme example, but events like scrolling and mouse movements usually have many functions associated with them, and we need some way of controlling the rate at which they’re called.

Debouncing is a technique used to make a function wait a certain amount of time before it can be called again. It’s often used in conjunction with events like scrolling, key presses, mouse movement, and other events that bring great overhead with them if they’re called every time they’re executed.

Let's revisit the example above. Here, we can add a debounce() function to our scroll event and add a delay to make sure that it can’t make too many calls to the back-end APIs. The debouncing function makes the system wait until the debounced event has not been called in a certain amount of time. Any calls prior to this time expiring are dropped. This avoids causing performance issues from the API and server. This solution can also be further improved if the client-side code keeps track of where the user is and when they have stopped scrolling so that the client only requests the data needed at that point.

An example debouncing

In the code snippet above, we declare a function called debounce() and provide it with the function  func() and the timeout value. The timeout variable is used to keep track of the current debounce timer. We set a new debounce timer using setTimeout. The timer waits the amount of milliseconds indicated in the wait variable before calling the original function func using apply and passing along the captured context and args.

Take a look at the diagram below:

Created with Fabric.js 3.6.6
The user begins sending requests to the server

1 of 7

Created with Fabric.js 3.6.6
The users sends more requests to the server and receives responses

2 of 7

Created with Fabric.js 3.6.6
The user is near the limit of the number of requests that can be sent within the defined threshold

3 of 7

Created with Fabric.js 3.6.6
The user has crossed the defined request threshold and now their requests will be dropped

4 of 7

Created with Fabric.js 3.6.6
The user sends another request but it dropped due to the debouncing being active

5 of 7

Created with Fabric.js 3.6.6
The user sends another request but it dropped due to the debouncing being active

6 of 7

Created with Fabric.js 3.6.6
The user's requests start going through and getting responses once the debounce timeout is over

7 of 7

In the diagram above, we see a client that is constantly requesting data from the server. As soon as the request threshold has been reached, the requests sent by the user are dropped and not entertained until the debounce time limit has been reached.

Summary #

We’ve seen how we can use resource hints to optimize our frontend to be able to better communicate with the back-end APIs. It also provides the user with their requested resources in a way that causes less latency to improve the overall UX of the application. However, it’s important to remember that we should use resource hints judiciously and only for resources that are truly critical to the current page or that will be needed in the future. Overusing resource hints can lead to increased resource loading overhead and negatively impact the overall performance of a website.

Speeding up Web Page Loading

Circuit Breaker Pattern